if(!require("plotly")) {install.packages("plotly")}

# install.packages("latex2exp")
# install.packages("BiocManager") 
# install.packages("corrplot")
# BiocManager::install("EBImage")

if(!require("lme4")){install.packages("lme4")}
if(!require("lmerTest")){install.packages("lmerTest")}
if(!require("nlme")){install.packages("nlme")}
if(!require("formattable")){install.packages("formattable")}
if(!require("xgboost")){install.packages("xgboost")}
if(!require("processx")) {install.packages("processx")}

library(plotly)
library(lme4)
library(lmerTest)
library(nlme)
library(formattable)
library(xgboost)

### Load libraries
library(EBImage)
library(ggplot2)
library(stringr)
library(gridExtra)
library(latex2exp)
packageVersion('plotly')
[1] ‘4.9.1’
Sys.setenv("plotly_username"="thuynh32")
Sys.setenv("plotly_api_key"="xcSv1yzujDc1IGEwQlr2")
colorBlue = "#007fff"
colorRed = "#ff7f7f"
colorGray = "#cccccc"
colorGreen = "#11ff00"

all_Drive4 <- read.csv('../../../data/TT1/preprocessed/All/TT1_Drive_4_1s_1s.csv')
all_Drive4$Subject <- as.factor(all_Drive4$Subject)
all_Drive4$logPerspiration <- log(all_Drive4$Perspiration)

persons         = c("01", "02", "03", "04", "05", "06", "07", "09", "12", "13", "15", "16", "17", "18", "22", "24", "29", "30", "31", "32", "41")
starting_points = c( 62 ,  83 ,  70 ,  69 ,  71 ,  61 ,  69 ,  74 ,  65 ,  61 ,  64 ,  63 ,  74 ,  65 ,  79 ,  64 ,  69 ,  69 ,  67 ,  65 ,  62 )
peak_points     = c( 65 ,  86 ,  73 ,  73 ,  73 ,  64 ,  73 ,  79 ,  69 ,  64 ,  68 ,  67 ,  77 ,  68 ,  82 ,  67 ,  72 ,  72 ,  71 ,  68 ,  64 )
ending_points   = c( 67 ,  89 ,  75 ,  76 ,  75 ,  67 ,  75 ,  82 ,  74 ,  66 ,  73 ,  70 ,  80 ,  71 ,  85 ,  70 ,  76 ,  75 ,  75 ,  70 ,  66 )

# Driving time
driving_times = vector(mode="list", length = length(persons))
names(driving_times) <- persons

complete_times = vector(mode="list", length = length(persons))
names(complete_times) <- persons

data_baseline = vector(mode="list", length=length(persons))
pp_baseline = vector(mode="list", length=length(persons))

names(data_baseline) <- persons
names(pp_baseline) <- persons

# Number of peaks

TRACKIN_DURATION = 10
DRIVE_MODE = 4
idx <- 1
plt_AllAcc <- vector(mode="list", length=length(persons)) 
names(plt_AllAcc) <- persons

COLOR_ACC = "#02A3C8"
COLOR_PP = "#F28E8E"
COLOR_BRAKE = "#888888"

y1 <- list(
  tickfont = list(color = COLOR_ACC),
  title="Degree",
  range=c(0, 100)
)
y2 <- list(
  tickfont = list(color = COLOR_PP),
  overlaying = "y",
  side = "right",
  title = "Log Perspiration",
  showgrid = FALSE,
  range=c(min(all_Drive4$ppLogNormalized), max(all_Drive4$ppLogNormalized))
)
  
for (p in persons) {
  pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
  
  # Baseline
  data_baseline[[p]] <- read.csv(str_interp("../../../data/TT1/preprocessed/T0${person}/T0${person}_Drive_1.csv", list(person=p)))
  # Compute the mean
  p_pp_nr <- data_baseline[[p]]$Perspiration
  p_pp_nr <- p_pp_nr[!is.na(p_pp_nr)]
  pp_baseline[[p]] <- log(mean(p_pp_nr))
  
  # Incident
  driving_times[[p]] <- max(pData$Time)
  complete_times[[p]] <- ifelse(starting_points[idx] + TRACKIN_DURATION > driving_times[[p]], driving_times[[p]], starting_points[idx] + TRACKIN_DURATION)
  
  incident_starting_time <- starting_points[idx]
  incident_ending_time <- ending_points[idx]
  complete_time <- complete_times[[p]]
  
  from_time <- ifelse(incident_starting_time - 30 >= 0, incident_starting_time - 30, 0)
  to_time <- complete_time
  
    
  pDataBefore <- pData[pData$Time < incident_starting_time & pData$Time >= from_time,]
  pDataAfter <- pData[pData$Time >= (incident_starting_time + 3) & pData$Time <= to_time,]
  
  ppMeanBefore <- mean(pDataBefore$ppLogNormalized)
  ppMeanAfter <- mean(pDataAfter$ppLogNormalized)
  
  dir.create(file.path('../figures/drive/', paste0('Drive_', DRIVE_MODE)), showWarnings = FALSE)
  fname <- str_interp('../figures/drive/Drive_${drive}/P${person}.svg', list(drive=DRIVE_MODE, person=p)) 
  
  pData <- pData[pData$Time >= from_time & pData$Time <= to_time,]
  plot_Acc <- plot_ly(pData, x = ~Time) %>%
              add_trace(name="Acceleration", y = ~Acceleration, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_ACC)) %>% 
              add_trace(name="Brake", y = ~Braking, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_BRAKE)) %>%
              add_trace(name="PP", y = ~ppLogNormalized, type = 'scatter', mode = 'lines', line=list(width=1.5, color=COLOR_PP), yaxis = "y2") %>% 
              add_segments(x = min(pData$Time), xend = max(pData$Time), y = ppMeanBefore, yend = ppMeanBefore, 
                           yaxis = "y2", name="Mean PP (Before Incident)",
                           line=list(color=COLOR_PP, dash = 'dot')) %>%
              add_segments(x = min(pData$Time), xend = max(pData$Time), y = ppMeanAfter, yend = ppMeanAfter, 
                           yaxis = "y2", name="Mean PP (After Incident)",
                           line=list(color="darkred", dash = 'dot')) %>%
              # add_segments(x = min(pData$Time) - 0.1, xend = max(pData$Time), y = pp_baseline[[p]], yend = pp_baseline[[p]], 
              #              yaxis = "y2", name="Baseline PP (from Drive 1)",
              #              line=list(color="blue", dash = 'dot')) %>%
    
              layout(
                title=paste("Subject #", p), 
                xaxis=list(title="Time [s]"), 
                yaxis=y1 , 
                yaxis2=y2, 
                margin = list(l = 50, r = 50, b = 50, t = 50, pad = 4),
                shapes = list(
                  list(type = "rect", fillcolor = "red", 
                       line = list(color = "red"), opacity = 0.3,
                      x0 = starting_points[idx], x1 = ending_points[idx], xref = "x",
                      y0 = 0, y1 = 100, yref = "y"),
                  list(type = "rect", fillcolor = "pink", 
                       line = list(color = "pink"), opacity = 0.3,
                      x0 = starting_points[idx], x1 = complete_times[[p]], xref = "x",
                      y0 = 0, y1 = 100, yref = "y")
                ),
                legend = list(x = min(pData$Time), y = 60)
              )
  
  # orca(plot_PP, fname)
  idx <- idx + 1
  plt_AllAcc[[p]] <- plot_Acc
}

htmltools::tagList(plt_AllAcc)
idx <- 1
behavioralColumns <- c("Subject", 
                       "Brake_u", 
                       "Brake_std", 
                       "PP_before",
                       "PP_u",  
                       "PP_std",
                       "PP_dev")
behavioralMatrix <- matrix(nrow=length(persons), ncol = length(behavioralColumns))

# Careful about Subject 09
# selected_persons <- persons[persons != "09"]

for (p in persons) {
  pData <- all_Drive4[all_Drive4$Subject==as.integer(p) | all_Drive4$Subject==p,]
  
  starting_time <- starting_points[idx]
  ending_time <- ending_points[idx]
  complete_time <- complete_times[[p]] 
  
  from_time <- ifelse(starting_time - 30 >= 0, starting_time - 30, 0)
  to_time <- complete_time
    
  dfBefore <- pData[pData$Time < starting_points[idx] & pData$Time >= from_time,]
  dfAfter <- pData[pData$Time >= starting_points[idx] + 3 & pData$Time <= to_time,]
  
  # diffSpeed <- mean(dfAfter$Speed) - mean(dfBefore$Speed)
  brakeMean <- mean(dfAfter$Braking)
  brakeStd <- mean(dfAfter$Braking)
  
  ppMean <- mean(dfAfter$ppLogNormalized)
  ppBefore <- mean(dfBefore$ppLogNormalized)
  ppStd <- sd(dfAfter$ppLogNormalized)
  
  mid_avg <- (pp_baseline[[p]] + mean(dfBefore$ppLogNormalized)) / 2
    
  diffPP <- mean(dfAfter$ppLogNormalized) - mean(dfBefore$ppLogNormalized)
  
  behavioralMatrix[idx, ] <- c(p, 
                               round(brakeMean, digits=5), 
                               round(brakeStd, digits=5),
                               round(ppBefore, digits=5),
                               round(ppMean, digits = 5),
                               round(ppStd, digits=5),
                               round(diffPP, digits=5))
  idx <- idx + 1
}

# behavioralMatrix

behavioralDf <- as.data.frame(behavioralMatrix)
names(behavioralDf) <- behavioralColumns

behavioralDf
NA
clusteringDf <- behavioralDf
clusteringDf$Subject <- NULL
# clusteringDf$PP_dev_norm <- as.numeric(clusteringDf$PP_dev) / as.numeric(clusteringDf$PP_u)
# clusteringDf$PP_std_norm <- as.numeric(clusteringDf$PP_std) / as.numeric(clusteringDf$PP_u)
clusteringDf$Brake_u <- NULL
clusteringDf$Brake_std <- NULL
clusteringDf$PP_before <- NULL
clusteringDf$PP_u <- NULL
clusteringDf$PP_std <- NULL
# clusteringDf$PP_dev <- NULL

rownames(clusteringDf) <- paste0("#", persons)

for (col in names(clusteringDf)) {
  clusteringDf[,col] <- as.numeric(as.character(clusteringDf[, col]))
  clusteringDf[,col] <- scale(clusteringDf[,col])
}
clusteringDf
library(dendextend)

NUMBER_OF_CLUSTERS = 3

color_darkpink = "#e75480"
CLUSTER_BRANCH_COLORS <- c("red", color_darkpink, "blue")[1:NUMBER_OF_CLUSTERS]
CLUSTER_LABEL_COLORS <- c("red", color_darkpink, "blue")[1:NUMBER_OF_CLUSTERS]

behavioralMatrixClustering <- as.matrix(clusteringDf)
rownames(behavioralMatrixClustering) <- paste0("#", persons)
distMatrix <- dist(behavioralMatrixClustering)
hresults <- distMatrix %>% hclust

hc <- hresults %>% 
      as.dendrogram %>%
      set("nodes_cex", NUMBER_OF_CLUSTERS) %>%
      set("labels_col", value = CLUSTER_LABEL_COLORS, k=NUMBER_OF_CLUSTERS) %>%
      # set("leaves_pch", 19) %>%
      # set("leaves_col", value = c("gray"), k=NUMBER_OF_CLUSTERS) %>%    
      set("branches_k_color", value=CLUSTER_BRANCH_COLORS, k=NUMBER_OF_CLUSTERS)

plot(hc)
legend("topright", 
     legend = c("Exceptional Chicken" , "Normal Chicken" , "No-change/Cool"), 
     col = c("red", "pink" , "blue"), 
     pch = c(20,20,20), bty = "n",  pt.cex = 1.5, cex = 0.8 , 
     text.col = "black", horiz = FALSE, inset = c(0, 0.1))

dfActivity <- all_Drive4[all_Drive4$Time==60,] %>% select(c("Subject", "Activity"))
getActivityName <- function(x) {
  if(x == 1) return("Normal")
  if(x == 2) return("Cognitive")
  if(x == 3) return("Motoric")
}
dfActivity$ActivityName <- sapply(dfActivity$Activity, getActivityName)
dfActivity$Subject <- as.factor(dfActivity$Subject)
rownames(dfActivity) <- NULL
dfActivity %>% select(c("Subject", "ActivityName"))
library(cluster)
fit <- kmeans(clusteringDf, 2)
clusplot(clusteringDf, fit$cluster, color=TRUE, shade=TRUE,
   labels=2, lines=0)

silhouette_score <- function(k){
  km <- kmeans(clusteringDf, centers = k, nstart=25)
  ss <- silhouette(km$cluster, dist(clusteringDf))
  mean(ss[, 3])
}
k <- 2:10
avg_sil <- sapply(k, silhouette_score)
plot(k, type='b', avg_sil, xlab='Number of clusters', ylab='Average Silhouette Scores', frame=FALSE)
# plot(clusteringDf$PP_Dev)
# ggplot(clusteringDf, aes(PP_Mean)) + 
#   geom_density(alpha = 0.3)
# plot(clusteringDf$PP_Dev)
# ggplot(clusteringDf, aes(PP_Dev)) + 
#   geom_density(alpha = 0.3)

ML Model

df_Drive3 <- read.csv('../data/output/Drive_3/corr_Prev_15s_Next_5s.csv')
df_Drive3$Subject <- as.factor(df_Drive3$Subject)

CLUSTER_THRESHOLD = 4
clusters <- cutree(hresults, h=CLUSTER_THRESHOLD)
getClusterName <- function(s) {
  sID <- str_replace(s, "Subject ", "")
  if (str_sub(sID[1], 1,1) != "#") {
    sID <- paste0("#", sID)
  }
  return(ifelse(clusters[sID] == 1, 1, 0))
}

colClass <- sapply(persons, getClusterName)
df_Drive3$clsPP <- as.factor(colClass)
# install.packages("randomForest")
# install.packages("MLmetrics")
# install.packages("caret")
library(randomForest)
library(ROCR)
library(caret)

# Train
trainAndTestModel <- function(pData) {
  pSelected <- pData
  pSelected$Subject <- NULL
  # pSelected$`Steering..std..` <- NULL
  
  # View(pSelected)
  
  nChicken = nrow(pSelected[pSelected$clsPP == 1,])
  nNormal = nrow(pSelected[pSelected$clsPP == 0,])
  
  print(paste("Chicken =", nChicken))
  print(paste("Normal =", nNormal))
  
  # Split dataset
  set.seed(43)
  n_folds = 4
  
  folds <- createFolds(factor(pSelected$clsPP), k = n_folds, list = FALSE)
  # folds <- cut(seq(1,nrow(pSelected)), breaks=n_folds,labels=FALSE)
  pSelected$fold <- folds
  
  kf_acc <- double(n_folds)
  kf_prec <- double(n_folds)
  kf_recall <- double(n_folds)
  kf_f1 <- double(n_folds)
  kf_npv <- double(n_folds)
  
  for (i in 1:n_folds) {
    # train_ind <- sample(seq_len(nrow(pSelected)), size = smp_size)
    # train <- pSelected[train_ind, ]
    # test <- pSelected[-train_ind, ]
    
    # test_ind <- which(folds==i, arr.ind=T)
    train <- pSelected[pSelected$fold != i, ] %>% select(-fold)
    test <- pSelected[pSelected$fold == i, ] %>% select(-fold)
    
    print(paste("Train =", nrow(train), "Pos =", nrow(train[train$clsPP == 1,]), "Neg =", nrow(train[train$clsPP == 0,])))
    print(paste("Test =", nrow(test), "Pos =", nrow(test[test$clsPP == 1,]), "Neg =", nrow(test[test$clsPP == 0,])))
    
    # Model Train
    model <- randomForest(clsPP~., data = train, importance = TRUE, ntree=10)
    print(importance(model))
    # print(model)
    
    # Test
    testX <- select(test, -clsPP)
    predY <- predict(model, testX)
    testY <- test$clsPP
    
    #print(levels(predY))
    #print(levels(testY))
    
    # Evaluate
    # print(table(predY, testY))
    
    kf_acc[i] <- mean(predY==testY)
    kf_recall[i] <- sensitivity(predY, testY)
    kf_prec[i] <- posPredValue(predY, testY, positive = 1)
    kf_f1[i] <- (2 * kf_recall[i] * kf_prec[i]) / (kf_recall[i] + kf_prec[i])
    kf_npv[i] <- negPredValue(predY, testY, positive = 1)
    
    # print(paste("Perf:", kf_acc[i], kf_recall[i], kf_prec[i], kf_f1[i], kf_npv[i]))
  }
  
  # XGB
  param <- list(objective       = "binary:logistic", 
               booster          = "gbtree",
               eval_metric      = "auc",
               eta              = 0.1,
               max_depth        = 5,
               gamma            =0.8,
               min_child_weight = 3,
               subsample        = 1,
               colsample_bytree = 0.5,
               stratified       = F
  )
  pSelected <- pSelected %>% mutate(clsPP=ifelse(clsPP==1, 1, 0))
  
  # AUC
  # aucs = c()
  # xgb_m = xgb.cv(   params               = param,
  #                   data = as.matrix(pSelected %>% select(-clsPP)) ,
  #                   label =  pSelected$clsPP,
  #                   nrounds             = 500,
  #                   verbose             = F,
  #                   prediction          = T,
  #                   maximize            = T,
  #                   nfold = n_folds,
  #                   metrics  = "auc",
  #                   early_stopping_rounds = 100,
  #                   scale_pos_weight = 1)
  # aucs =  c(aucs,as.numeric(xgb_m$evaluation_log[xgb_m$best_iteration,"test_auc_mean"]))
  
  # Get average performance
  acc <- mean(kf_acc)
  prec <- mean(kf_prec)
  recall <- mean(kf_recall)
  f1 <- mean(kf_f1)
  npv <- mean(kf_npv)
  # auc <- mean(aucs)
 
  # Return 
  rtn <- list(
    accuracy=acc,
    recall=recall,
    precision=prec,
    f1=f1,
    npv=npv
    # auc=auc
  )
  
  print(rtn)
}

trainAndTestModel(df_Drive3)
library(caret)
library(VGAM)

NUM_FEATURES = 8

pSelected <- df_Drive3
pSelected$Subject <- NULL

fit <- vglm(clsPP~., family=multinomial, data=pSelected)
# summarize the fit
summary(fit)

probabilities <- predict(fit, pSelected[,1:NUM_FEATURES], type="response")
predictions <- apply(probabilities, 1, which.max)
predictions[which(predictions=="High")] <- levels(pSelected$clsPP)[1]
predictions[which(predictions=="Low")] <- levels(pSelected$clsPP)[2]
# summarize accuracy
table(predictions, pSelected$clsPP)

# x <- pSelected[,1:NUM_FEATURES]
# y <- pSelected[,NUM_FEATURES + 1]
# # fit model
# fit <- plsda(x, y, probMethod="Bayes")
# # summarize the fit
# summary(fit)
# # make predictions
# predictions <- predict(fit, pSelected[,1:NUM_FEATURES])
# # summarize accuracy
# table(predictions, pSelected$clsPP)
library(caret)

pSelected <- df_Drive3
pSelected$Subject <- NULL
pSelected$clsPP <- as.numeric(colClass)

# define training control
train_control <- trainControl(method = "cv", number = 4)

# train the model on training set
model <- train(clsPP ~ .,
               data = pSelected,
               trControl = train_control,
               method = "glm",
               family=binomial())

# print cv scores
summary(model)

predTrain = predict(model, newdata=pSelected, type="raw")
table(pSelected$clsPP, predTrain > 0.5)
formula = clsPP ~ .
modellm <- glm(formula, data=pSelected)
plot(modellm)
summary(model)
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKYGBge3J9CmlmKCFyZXF1aXJlKCJwbG90bHkiKSkge2luc3RhbGwucGFja2FnZXMoInBsb3RseSIpfQoKIyBpbnN0YWxsLnBhY2thZ2VzKCJsYXRleDJleHAiKQojIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikgCiMgaW5zdGFsbC5wYWNrYWdlcygiY29ycnBsb3QiKQojIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJFQkltYWdlIikKCmlmKCFyZXF1aXJlKCJsbWU0Iikpe2luc3RhbGwucGFja2FnZXMoImxtZTQiKX0KaWYoIXJlcXVpcmUoImxtZXJUZXN0Iikpe2luc3RhbGwucGFja2FnZXMoImxtZXJUZXN0Iil9CmlmKCFyZXF1aXJlKCJubG1lIikpe2luc3RhbGwucGFja2FnZXMoIm5sbWUiKX0KaWYoIXJlcXVpcmUoImZvcm1hdHRhYmxlIikpe2luc3RhbGwucGFja2FnZXMoImZvcm1hdHRhYmxlIil9CmlmKCFyZXF1aXJlKCJ4Z2Jvb3N0Iikpe2luc3RhbGwucGFja2FnZXMoInhnYm9vc3QiKX0KaWYoIXJlcXVpcmUoInByb2Nlc3N4IikpIHtpbnN0YWxsLnBhY2thZ2VzKCJwcm9jZXNzeCIpfQoKbGlicmFyeShwbG90bHkpCmxpYnJhcnkobG1lNCkKbGlicmFyeShsbWVyVGVzdCkKbGlicmFyeShubG1lKQpsaWJyYXJ5KGZvcm1hdHRhYmxlKQpsaWJyYXJ5KHhnYm9vc3QpCgojIyMgTG9hZCBsaWJyYXJpZXMKbGlicmFyeShFQkltYWdlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShncmlkRXh0cmEpCmxpYnJhcnkobGF0ZXgyZXhwKQpwYWNrYWdlVmVyc2lvbigncGxvdGx5JykKU3lzLnNldGVudigicGxvdGx5X3VzZXJuYW1lIj0idGh1eW5oMzIiKQpTeXMuc2V0ZW52KCJwbG90bHlfYXBpX2tleSI9InhjU3YxeXp1akRjMUlHRXdRbHIyIikKCmBgYAoKYGBge3J9CmNvbG9yQmx1ZSA9ICIjMDA3ZmZmIgpjb2xvclJlZCA9ICIjZmY3ZjdmIgpjb2xvckdyYXkgPSAiI2NjY2NjYyIKY29sb3JHcmVlbiA9ICIjMTFmZjAwIgoKYWxsX0RyaXZlNCA8LSByZWFkLmNzdignLi4vLi4vLi4vZGF0YS9UVDEvcHJlcHJvY2Vzc2VkL0FsbC9UVDFfRHJpdmVfNF8xc18xcy5jc3YnKQphbGxfRHJpdmU0JFN1YmplY3QgPC0gYXMuZmFjdG9yKGFsbF9Ecml2ZTQkU3ViamVjdCkKYWxsX0RyaXZlNCRsb2dQZXJzcGlyYXRpb24gPC0gbG9nKGFsbF9Ecml2ZTQkUGVyc3BpcmF0aW9uKQoKcGVyc29ucyAgICAgICAgID0gYygiMDEiLCAiMDIiLCAiMDMiLCAiMDQiLCAiMDUiLCAiMDYiLCAiMDciLCAiMDkiLCAiMTIiLCAiMTMiLCAiMTUiLCAiMTYiLCAiMTciLCAiMTgiLCAiMjIiLCAiMjQiLCAiMjkiLCAiMzAiLCAiMzEiLCAiMzIiLCAiNDEiKQpzdGFydGluZ19wb2ludHMgPSBjKCA2MiAsICA4MyAsICA3MCAsICA2OSAsICA3MSAsICA2MSAsICA2OSAsICA3NCAsICA2NSAsICA2MSAsICA2NCAsICA2MyAsICA3NCAsICA2NSAsICA3OSAsICA2NCAsICA2OSAsICA2OSAsICA2NyAsICA2NSAsICA2MiApCnBlYWtfcG9pbnRzICAgICA9IGMoIDY1ICwgIDg2ICwgIDczICwgIDczICwgIDczICwgIDY0ICwgIDczICwgIDc5ICwgIDY5ICwgIDY0ICwgIDY4ICwgIDY3ICwgIDc3ICwgIDY4ICwgIDgyICwgIDY3ICwgIDcyICwgIDcyICwgIDcxICwgIDY4ICwgIDY0ICkKZW5kaW5nX3BvaW50cyAgID0gYyggNjcgLCAgODkgLCAgNzUgLCAgNzYgLCAgNzUgLCAgNjcgLCAgNzUgLCAgODIgLCAgNzQgLCAgNjYgLCAgNzMgLCAgNzAgLCAgODAgLCAgNzEgLCAgODUgLCAgNzAgLCAgNzYgLCAgNzUgLCAgNzUgLCAgNzAgLCAgNjYgKQoKIyBEcml2aW5nIHRpbWUKZHJpdmluZ190aW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhkcml2aW5nX3RpbWVzKSA8LSBwZXJzb25zCgpjb21wbGV0ZV90aW1lcyA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoID0gbGVuZ3RoKHBlcnNvbnMpKQpuYW1lcyhjb21wbGV0ZV90aW1lcykgPC0gcGVyc29ucwoKZGF0YV9iYXNlbGluZSA9IHZlY3Rvcihtb2RlPSJsaXN0IiwgbGVuZ3RoPWxlbmd0aChwZXJzb25zKSkKcHBfYmFzZWxpbmUgPSB2ZWN0b3IobW9kZT0ibGlzdCIsIGxlbmd0aD1sZW5ndGgocGVyc29ucykpCgpuYW1lcyhkYXRhX2Jhc2VsaW5lKSA8LSBwZXJzb25zCm5hbWVzKHBwX2Jhc2VsaW5lKSA8LSBwZXJzb25zCgojIE51bWJlciBvZiBwZWFrcwoKVFJBQ0tJTl9EVVJBVElPTiA9IDEwCkRSSVZFX01PREUgPSA0CmBgYAoKYGBge3J9CmlkeCA8LSAxCnBsdF9BbGxBY2MgPC0gdmVjdG9yKG1vZGU9Imxpc3QiLCBsZW5ndGg9bGVuZ3RoKHBlcnNvbnMpKSAKbmFtZXMocGx0X0FsbEFjYykgPC0gcGVyc29ucwoKQ09MT1JfQUNDID0gIiMwMkEzQzgiCkNPTE9SX1BQID0gIiNGMjhFOEUiCkNPTE9SX0JSQUtFID0gIiM4ODg4ODgiCgp5MSA8LSBsaXN0KAogIHRpY2tmb250ID0gbGlzdChjb2xvciA9IENPTE9SX0FDQyksCiAgdGl0bGU9IkRlZ3JlZSIsCiAgcmFuZ2U9YygwLCAxMDApCikKeTIgPC0gbGlzdCgKICB0aWNrZm9udCA9IGxpc3QoY29sb3IgPSBDT0xPUl9QUCksCiAgb3ZlcmxheWluZyA9ICJ5IiwKICBzaWRlID0gInJpZ2h0IiwKICB0aXRsZSA9ICJMb2cgUGVyc3BpcmF0aW9uIiwKICBzaG93Z3JpZCA9IEZBTFNFLAogIHJhbmdlPWMobWluKGFsbF9Ecml2ZTQkcHBMb2dOb3JtYWxpemVkKSwgbWF4KGFsbF9Ecml2ZTQkcHBMb2dOb3JtYWxpemVkKSkKKQogIApmb3IgKHAgaW4gcGVyc29ucykgewogIHBEYXRhIDwtIGFsbF9Ecml2ZTRbYWxsX0RyaXZlNCRTdWJqZWN0PT1hcy5pbnRlZ2VyKHApIHwgYWxsX0RyaXZlNCRTdWJqZWN0PT1wLF0KICAKICAjIEJhc2VsaW5lCiAgZGF0YV9iYXNlbGluZVtbcF1dIDwtIHJlYWQuY3N2KHN0cl9pbnRlcnAoIi4uLy4uLy4uL2RhdGEvVFQxL3ByZXByb2Nlc3NlZC9UMCR7cGVyc29ufS9UMCR7cGVyc29ufV9Ecml2ZV8xLmNzdiIsIGxpc3QocGVyc29uPXApKSkKICAjIENvbXB1dGUgdGhlIG1lYW4KICBwX3BwX25yIDwtIGRhdGFfYmFzZWxpbmVbW3BdXSRQZXJzcGlyYXRpb24KICBwX3BwX25yIDwtIHBfcHBfbnJbIWlzLm5hKHBfcHBfbnIpXQogIHBwX2Jhc2VsaW5lW1twXV0gPC0gbG9nKG1lYW4ocF9wcF9ucikpCiAgCiAgIyBJbmNpZGVudAogIGRyaXZpbmdfdGltZXNbW3BdXSA8LSBtYXgocERhdGEkVGltZSkKICBjb21wbGV0ZV90aW1lc1tbcF1dIDwtIGlmZWxzZShzdGFydGluZ19wb2ludHNbaWR4XSArIFRSQUNLSU5fRFVSQVRJT04gPiBkcml2aW5nX3RpbWVzW1twXV0sIGRyaXZpbmdfdGltZXNbW3BdXSwgc3RhcnRpbmdfcG9pbnRzW2lkeF0gKyBUUkFDS0lOX0RVUkFUSU9OKQogIAogIGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgPC0gc3RhcnRpbmdfcG9pbnRzW2lkeF0KICBpbmNpZGVudF9lbmRpbmdfdGltZSA8LSBlbmRpbmdfcG9pbnRzW2lkeF0KICBjb21wbGV0ZV90aW1lIDwtIGNvbXBsZXRlX3RpbWVzW1twXV0KICAKICBmcm9tX3RpbWUgPC0gaWZlbHNlKGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgLSAzMCA+PSAwLCBpbmNpZGVudF9zdGFydGluZ190aW1lIC0gMzAsIDApCiAgdG9fdGltZSA8LSBjb21wbGV0ZV90aW1lCiAgCiAgICAKICBwRGF0YUJlZm9yZSA8LSBwRGF0YVtwRGF0YSRUaW1lIDwgaW5jaWRlbnRfc3RhcnRpbmdfdGltZSAmIHBEYXRhJFRpbWUgPj0gZnJvbV90aW1lLF0KICBwRGF0YUFmdGVyIDwtIHBEYXRhW3BEYXRhJFRpbWUgPj0gKGluY2lkZW50X3N0YXJ0aW5nX3RpbWUgKyAzKSAmIHBEYXRhJFRpbWUgPD0gdG9fdGltZSxdCiAgCiAgcHBNZWFuQmVmb3JlIDwtIG1lYW4ocERhdGFCZWZvcmUkcHBMb2dOb3JtYWxpemVkKQogIHBwTWVhbkFmdGVyIDwtIG1lYW4ocERhdGFBZnRlciRwcExvZ05vcm1hbGl6ZWQpCiAgCiAgZGlyLmNyZWF0ZShmaWxlLnBhdGgoJy4uL2ZpZ3VyZXMvZHJpdmUvJywgcGFzdGUwKCdEcml2ZV8nLCBEUklWRV9NT0RFKSksIHNob3dXYXJuaW5ncyA9IEZBTFNFKQogIGZuYW1lIDwtIHN0cl9pbnRlcnAoJy4uL2ZpZ3VyZXMvZHJpdmUvRHJpdmVfJHtkcml2ZX0vUCR7cGVyc29ufS5zdmcnLCBsaXN0KGRyaXZlPURSSVZFX01PREUsIHBlcnNvbj1wKSkgCiAgCiAgcERhdGEgPC0gcERhdGFbcERhdGEkVGltZSA+PSBmcm9tX3RpbWUgJiBwRGF0YSRUaW1lIDw9IHRvX3RpbWUsXQogIHBsb3RfQWNjIDwtIHBsb3RfbHkocERhdGEsIHggPSB+VGltZSkgJT4lCiAgICAgICAgICAgICAgYWRkX3RyYWNlKG5hbWU9IkFjY2VsZXJhdGlvbiIsIHkgPSB+QWNjZWxlcmF0aW9uLCB0eXBlID0gJ3NjYXR0ZXInLCBtb2RlID0gJ2xpbmVzJywgbGluZT1saXN0KHdpZHRoPTEuNSwgY29sb3I9Q09MT1JfQUNDKSkgJT4lIAogICAgICAgICAgICAgIGFkZF90cmFjZShuYW1lPSJCcmFrZSIsIHkgPSB+QnJha2luZywgdHlwZSA9ICdzY2F0dGVyJywgbW9kZSA9ICdsaW5lcycsIGxpbmU9bGlzdCh3aWR0aD0xLjUsIGNvbG9yPUNPTE9SX0JSQUtFKSkgJT4lCiAgICAgICAgICAgICAgYWRkX3RyYWNlKG5hbWU9IlBQIiwgeSA9IH5wcExvZ05vcm1hbGl6ZWQsIHR5cGUgPSAnc2NhdHRlcicsIG1vZGUgPSAnbGluZXMnLCBsaW5lPWxpc3Qod2lkdGg9MS41LCBjb2xvcj1DT0xPUl9QUCksIHlheGlzID0gInkyIikgJT4lIAogICAgICAgICAgICAgIGFkZF9zZWdtZW50cyh4ID0gbWluKHBEYXRhJFRpbWUpLCB4ZW5kID0gbWF4KHBEYXRhJFRpbWUpLCB5ID0gcHBNZWFuQmVmb3JlLCB5ZW5kID0gcHBNZWFuQmVmb3JlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSAieTIiLCBuYW1lPSJNZWFuIFBQIChCZWZvcmUgSW5jaWRlbnQpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZT1saXN0KGNvbG9yPUNPTE9SX1BQLCBkYXNoID0gJ2RvdCcpKSAlPiUKICAgICAgICAgICAgICBhZGRfc2VnbWVudHMoeCA9IG1pbihwRGF0YSRUaW1lKSwgeGVuZCA9IG1heChwRGF0YSRUaW1lKSwgeSA9IHBwTWVhbkFmdGVyLCB5ZW5kID0gcHBNZWFuQWZ0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9ICJ5MiIsIG5hbWU9Ik1lYW4gUFAgKEFmdGVyIEluY2lkZW50KSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmU9bGlzdChjb2xvcj0iZGFya3JlZCIsIGRhc2ggPSAnZG90JykpICU+JQogICAgICAgICAgICAgICMgYWRkX3NlZ21lbnRzKHggPSBtaW4ocERhdGEkVGltZSkgLSAwLjEsIHhlbmQgPSBtYXgocERhdGEkVGltZSksIHkgPSBwcF9iYXNlbGluZVtbcF1dLCB5ZW5kID0gcHBfYmFzZWxpbmVbW3BdXSwgCiAgICAgICAgICAgICAgIyAgICAgICAgICAgICAgeWF4aXMgPSAieTIiLCBuYW1lPSJCYXNlbGluZSBQUCAoZnJvbSBEcml2ZSAxKSIsCiAgICAgICAgICAgICAgIyAgICAgICAgICAgICAgbGluZT1saXN0KGNvbG9yPSJibHVlIiwgZGFzaCA9ICdkb3QnKSkgJT4lCiAgICAKICAgICAgICAgICAgICBsYXlvdXQoCiAgICAgICAgICAgICAgICB0aXRsZT1wYXN0ZSgiU3ViamVjdCAjIiwgcCksIAogICAgICAgICAgICAgICAgeGF4aXM9bGlzdCh0aXRsZT0iVGltZSBbc10iKSwgCiAgICAgICAgICAgICAgICB5YXhpcz15MSAsIAogICAgICAgICAgICAgICAgeWF4aXMyPXkyLCAKICAgICAgICAgICAgICAgIG1hcmdpbiA9IGxpc3QobCA9IDUwLCByID0gNTAsIGIgPSA1MCwgdCA9IDUwLCBwYWQgPSA0KSwKICAgICAgICAgICAgICAgIHNoYXBlcyA9IGxpc3QoCiAgICAgICAgICAgICAgICAgIGxpc3QodHlwZSA9ICJyZWN0IiwgZmlsbGNvbG9yID0gInJlZCIsIAogICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInJlZCIpLCBvcGFjaXR5ID0gMC4zLAogICAgICAgICAgICAgICAgICAgICAgeDAgPSBzdGFydGluZ19wb2ludHNbaWR4XSwgeDEgPSBlbmRpbmdfcG9pbnRzW2lkeF0sIHhyZWYgPSAieCIsCiAgICAgICAgICAgICAgICAgICAgICB5MCA9IDAsIHkxID0gMTAwLCB5cmVmID0gInkiKSwKICAgICAgICAgICAgICAgICAgbGlzdCh0eXBlID0gInJlY3QiLCBmaWxsY29sb3IgPSAicGluayIsIAogICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gInBpbmsiKSwgb3BhY2l0eSA9IDAuMywKICAgICAgICAgICAgICAgICAgICAgIHgwID0gc3RhcnRpbmdfcG9pbnRzW2lkeF0sIHgxID0gY29tcGxldGVfdGltZXNbW3BdXSwgeHJlZiA9ICJ4IiwKICAgICAgICAgICAgICAgICAgICAgIHkwID0gMCwgeTEgPSAxMDAsIHlyZWYgPSAieSIpCiAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgbGVnZW5kID0gbGlzdCh4ID0gbWluKHBEYXRhJFRpbWUpLCB5ID0gNjApCiAgICAgICAgICAgICAgKQogIAogICMgb3JjYShwbG90X1BQLCBmbmFtZSkKICBpZHggPC0gaWR4ICsgMQogIHBsdF9BbGxBY2NbW3BdXSA8LSBwbG90X0FjYwp9CgpodG1sdG9vbHM6OnRhZ0xpc3QocGx0X0FsbEFjYykKYGBgCgoKYGBge3J9CmlkeCA8LSAxCmJlaGF2aW9yYWxDb2x1bW5zIDwtIGMoIlN1YmplY3QiLCAKICAgICAgICAgICAgICAgICAgICAgICAiQnJha2VfdSIsIAogICAgICAgICAgICAgICAgICAgICAgICJCcmFrZV9zdGQiLCAKICAgICAgICAgICAgICAgICAgICAgICAiUFBfYmVmb3JlIiwKICAgICAgICAgICAgICAgICAgICAgICAiUFBfdSIsICAKICAgICAgICAgICAgICAgICAgICAgICAiUFBfc3RkIiwKICAgICAgICAgICAgICAgICAgICAgICAiUFBfZGV2IikKYmVoYXZpb3JhbE1hdHJpeCA8LSBtYXRyaXgobnJvdz1sZW5ndGgocGVyc29ucyksIG5jb2wgPSBsZW5ndGgoYmVoYXZpb3JhbENvbHVtbnMpKQoKIyBDYXJlZnVsIGFib3V0IFN1YmplY3QgMDkKIyBzZWxlY3RlZF9wZXJzb25zIDwtIHBlcnNvbnNbcGVyc29ucyAhPSAiMDkiXQoKZm9yIChwIGluIHBlcnNvbnMpIHsKICBwRGF0YSA8LSBhbGxfRHJpdmU0W2FsbF9Ecml2ZTQkU3ViamVjdD09YXMuaW50ZWdlcihwKSB8IGFsbF9Ecml2ZTQkU3ViamVjdD09cCxdCiAgCiAgc3RhcnRpbmdfdGltZSA8LSBzdGFydGluZ19wb2ludHNbaWR4XQogIGVuZGluZ190aW1lIDwtIGVuZGluZ19wb2ludHNbaWR4XQogIGNvbXBsZXRlX3RpbWUgPC0gY29tcGxldGVfdGltZXNbW3BdXSAKICAKICBmcm9tX3RpbWUgPC0gaWZlbHNlKHN0YXJ0aW5nX3RpbWUgLSAzMCA+PSAwLCBzdGFydGluZ190aW1lIC0gMzAsIDApCiAgdG9fdGltZSA8LSBjb21wbGV0ZV90aW1lCiAgICAKICBkZkJlZm9yZSA8LSBwRGF0YVtwRGF0YSRUaW1lIDwgc3RhcnRpbmdfcG9pbnRzW2lkeF0gJiBwRGF0YSRUaW1lID49IGZyb21fdGltZSxdCiAgZGZBZnRlciA8LSBwRGF0YVtwRGF0YSRUaW1lID49IHN0YXJ0aW5nX3BvaW50c1tpZHhdICsgMyAmIHBEYXRhJFRpbWUgPD0gdG9fdGltZSxdCiAgCiAgIyBkaWZmU3BlZWQgPC0gbWVhbihkZkFmdGVyJFNwZWVkKSAtIG1lYW4oZGZCZWZvcmUkU3BlZWQpCiAgYnJha2VNZWFuIDwtIG1lYW4oZGZBZnRlciRCcmFraW5nKQogIGJyYWtlU3RkIDwtIG1lYW4oZGZBZnRlciRCcmFraW5nKQogIAogIHBwTWVhbiA8LSBtZWFuKGRmQWZ0ZXIkcHBMb2dOb3JtYWxpemVkKQogIHBwQmVmb3JlIDwtIG1lYW4oZGZCZWZvcmUkcHBMb2dOb3JtYWxpemVkKQogIHBwU3RkIDwtIHNkKGRmQWZ0ZXIkcHBMb2dOb3JtYWxpemVkKQogIAogIG1pZF9hdmcgPC0gKHBwX2Jhc2VsaW5lW1twXV0gKyBtZWFuKGRmQmVmb3JlJHBwTG9nTm9ybWFsaXplZCkpIC8gMgogICAgCiAgZGlmZlBQIDwtIG1lYW4oZGZBZnRlciRwcExvZ05vcm1hbGl6ZWQpIC0gbWVhbihkZkJlZm9yZSRwcExvZ05vcm1hbGl6ZWQpCiAgCiAgYmVoYXZpb3JhbE1hdHJpeFtpZHgsIF0gPC0gYyhwLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGJyYWtlTWVhbiwgZGlnaXRzPTUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKGJyYWtlU3RkLCBkaWdpdHM9NSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChwcEJlZm9yZSwgZGlnaXRzPTUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcm91bmQocHBNZWFuLCBkaWdpdHMgPSA1KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvdW5kKHBwU3RkLCBkaWdpdHM9NSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByb3VuZChkaWZmUFAsIGRpZ2l0cz01KSkKICBpZHggPC0gaWR4ICsgMQp9CgojIGJlaGF2aW9yYWxNYXRyaXgKCmJlaGF2aW9yYWxEZiA8LSBhcy5kYXRhLmZyYW1lKGJlaGF2aW9yYWxNYXRyaXgpCm5hbWVzKGJlaGF2aW9yYWxEZikgPC0gYmVoYXZpb3JhbENvbHVtbnMKCmJlaGF2aW9yYWxEZgoKYGBgCgoKYGBge3J9CmNsdXN0ZXJpbmdEZiA8LSBiZWhhdmlvcmFsRGYKY2x1c3RlcmluZ0RmJFN1YmplY3QgPC0gTlVMTAojIGNsdXN0ZXJpbmdEZiRQUF9kZXZfbm9ybSA8LSBhcy5udW1lcmljKGNsdXN0ZXJpbmdEZiRQUF9kZXYpIC8gYXMubnVtZXJpYyhjbHVzdGVyaW5nRGYkUFBfdSkKIyBjbHVzdGVyaW5nRGYkUFBfc3RkX25vcm0gPC0gYXMubnVtZXJpYyhjbHVzdGVyaW5nRGYkUFBfc3RkKSAvIGFzLm51bWVyaWMoY2x1c3RlcmluZ0RmJFBQX3UpCmNsdXN0ZXJpbmdEZiRCcmFrZV91IDwtIE5VTEwKY2x1c3RlcmluZ0RmJEJyYWtlX3N0ZCA8LSBOVUxMCmNsdXN0ZXJpbmdEZiRQUF9iZWZvcmUgPC0gTlVMTApjbHVzdGVyaW5nRGYkUFBfdSA8LSBOVUxMCmNsdXN0ZXJpbmdEZiRQUF9zdGQgPC0gTlVMTAojIGNsdXN0ZXJpbmdEZiRQUF9kZXYgPC0gTlVMTAoKcm93bmFtZXMoY2x1c3RlcmluZ0RmKSA8LSBwYXN0ZTAoIiMiLCBwZXJzb25zKQoKZm9yIChjb2wgaW4gbmFtZXMoY2x1c3RlcmluZ0RmKSkgewogIGNsdXN0ZXJpbmdEZlssY29sXSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihjbHVzdGVyaW5nRGZbLCBjb2xdKSkKICBjbHVzdGVyaW5nRGZbLGNvbF0gPC0gc2NhbGUoY2x1c3RlcmluZ0RmWyxjb2xdKQp9CmNsdXN0ZXJpbmdEZgpgYGAKCmBgYHtyfQpsaWJyYXJ5KGRlbmRleHRlbmQpCgpOVU1CRVJfT0ZfQ0xVU1RFUlMgPSAzCgpjb2xvcl9kYXJrcGluayA9ICIjZTc1NDgwIgpDTFVTVEVSX0JSQU5DSF9DT0xPUlMgPC0gYygicmVkIiwgY29sb3JfZGFya3BpbmssICJibHVlIilbMTpOVU1CRVJfT0ZfQ0xVU1RFUlNdCkNMVVNURVJfTEFCRUxfQ09MT1JTIDwtIGMoInJlZCIsIGNvbG9yX2RhcmtwaW5rLCAiYmx1ZSIpWzE6TlVNQkVSX09GX0NMVVNURVJTXQoKYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcgPC0gYXMubWF0cml4KGNsdXN0ZXJpbmdEZikKcm93bmFtZXMoYmVoYXZpb3JhbE1hdHJpeENsdXN0ZXJpbmcpIDwtIHBhc3RlMCgiIyIsIHBlcnNvbnMpCmRpc3RNYXRyaXggPC0gZGlzdChiZWhhdmlvcmFsTWF0cml4Q2x1c3RlcmluZykKaHJlc3VsdHMgPC0gZGlzdE1hdHJpeCAlPiUgaGNsdXN0CgpoYyA8LSBocmVzdWx0cyAlPiUgCiAgICAgIGFzLmRlbmRyb2dyYW0gJT4lCiAgICAgIHNldCgibm9kZXNfY2V4IiwgTlVNQkVSX09GX0NMVVNURVJTKSAlPiUKICAgICAgc2V0KCJsYWJlbHNfY29sIiwgdmFsdWUgPSBDTFVTVEVSX0xBQkVMX0NPTE9SUywgaz1OVU1CRVJfT0ZfQ0xVU1RFUlMpICU+JQogICAgICAjIHNldCgibGVhdmVzX3BjaCIsIDE5KSAlPiUKICAgICAgIyBzZXQoImxlYXZlc19jb2wiLCB2YWx1ZSA9IGMoImdyYXkiKSwgaz1OVU1CRVJfT0ZfQ0xVU1RFUlMpICU+JSAgICAKICAgICAgc2V0KCJicmFuY2hlc19rX2NvbG9yIiwgdmFsdWU9Q0xVU1RFUl9CUkFOQ0hfQ09MT1JTLCBrPU5VTUJFUl9PRl9DTFVTVEVSUykKCnBsb3QoaGMpCmxlZ2VuZCgidG9wcmlnaHQiLCAKICAgICBsZWdlbmQgPSBjKCJFeGNlcHRpb25hbCBDaGlja2VuIiAsICJOb3JtYWwgQ2hpY2tlbiIgLCAiTm8tY2hhbmdlL0Nvb2wiKSwgCiAgICAgY29sID0gYygicmVkIiwgInBpbmsiICwgImJsdWUiKSwgCiAgICAgcGNoID0gYygyMCwyMCwyMCksIGJ0eSA9ICJuIiwgIHB0LmNleCA9IDEuNSwgY2V4ID0gMC44ICwgCiAgICAgdGV4dC5jb2wgPSAiYmxhY2siLCBob3JpeiA9IEZBTFNFLCBpbnNldCA9IGMoMCwgMC4xKSkKYGBgCmBgYHtyfQpkZkFjdGl2aXR5IDwtIGFsbF9Ecml2ZTRbYWxsX0RyaXZlNCRUaW1lPT02MCxdICU+JSBzZWxlY3QoYygiU3ViamVjdCIsICJBY3Rpdml0eSIpKQpnZXRBY3Rpdml0eU5hbWUgPC0gZnVuY3Rpb24oeCkgewogIGlmKHggPT0gMSkgcmV0dXJuKCJOb3JtYWwiKQogIGlmKHggPT0gMikgcmV0dXJuKCJDb2duaXRpdmUiKQogIGlmKHggPT0gMykgcmV0dXJuKCJNb3RvcmljIikKfQpkZkFjdGl2aXR5JEFjdGl2aXR5TmFtZSA8LSBzYXBwbHkoZGZBY3Rpdml0eSRBY3Rpdml0eSwgZ2V0QWN0aXZpdHlOYW1lKQpkZkFjdGl2aXR5JFN1YmplY3QgPC0gYXMuZmFjdG9yKGRmQWN0aXZpdHkkU3ViamVjdCkKcm93bmFtZXMoZGZBY3Rpdml0eSkgPC0gTlVMTApkZkFjdGl2aXR5ICU+JSBzZWxlY3QoYygiU3ViamVjdCIsICJBY3Rpdml0eU5hbWUiKSkKYGBgCgpgYGB7cn0KbGlicmFyeShjbHVzdGVyKQpmaXQgPC0ga21lYW5zKGNsdXN0ZXJpbmdEZiwgMikKY2x1c3Bsb3QoY2x1c3RlcmluZ0RmLCBmaXQkY2x1c3RlciwgY29sb3I9VFJVRSwgc2hhZGU9VFJVRSwKICAgbGFiZWxzPTIsIGxpbmVzPTApCmBgYAoKYGBge3J9CnNpbGhvdWV0dGVfc2NvcmUgPC0gZnVuY3Rpb24oayl7CiAga20gPC0ga21lYW5zKGNsdXN0ZXJpbmdEZiwgY2VudGVycyA9IGssIG5zdGFydD0yNSkKICBzcyA8LSBzaWxob3VldHRlKGttJGNsdXN0ZXIsIGRpc3QoY2x1c3RlcmluZ0RmKSkKICBtZWFuKHNzWywgM10pCn0KayA8LSAyOjEwCmF2Z19zaWwgPC0gc2FwcGx5KGssIHNpbGhvdWV0dGVfc2NvcmUpCnBsb3QoaywgdHlwZT0nYicsIGF2Z19zaWwsIHhsYWI9J051bWJlciBvZiBjbHVzdGVycycsIHlsYWI9J0F2ZXJhZ2UgU2lsaG91ZXR0ZSBTY29yZXMnLCBmcmFtZT1GQUxTRSkKYGBgCgoKYGBge3J9CiMgcGxvdChjbHVzdGVyaW5nRGYkUFBfRGV2KQojIGdncGxvdChjbHVzdGVyaW5nRGYsIGFlcyhQUF9NZWFuKSkgKyAKIyAgIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMykKYGBgCgpgYGB7cn0KIyBwbG90KGNsdXN0ZXJpbmdEZiRQUF9EZXYpCiMgZ2dwbG90KGNsdXN0ZXJpbmdEZiwgYWVzKFBQX0RldikpICsgCiMgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjMpCmBgYAoKIyMjIE1MIE1vZGVsCmBgYHtyfQpkZl9Ecml2ZTMgPC0gcmVhZC5jc3YoJy4uL2RhdGEvb3V0cHV0L0RyaXZlXzMvY29ycl9QcmV2XzE1c19OZXh0XzVzLmNzdicpCmRmX0RyaXZlMyRTdWJqZWN0IDwtIGFzLmZhY3RvcihkZl9Ecml2ZTMkU3ViamVjdCkKCkNMVVNURVJfVEhSRVNIT0xEID0gNApjbHVzdGVycyA8LSBjdXRyZWUoaHJlc3VsdHMsIGg9Q0xVU1RFUl9USFJFU0hPTEQpCmdldENsdXN0ZXJOYW1lIDwtIGZ1bmN0aW9uKHMpIHsKICBzSUQgPC0gc3RyX3JlcGxhY2UocywgIlN1YmplY3QgIiwgIiIpCiAgaWYgKHN0cl9zdWIoc0lEWzFdLCAxLDEpICE9ICIjIikgewogICAgc0lEIDwtIHBhc3RlMCgiIyIsIHNJRCkKICB9CiAgcmV0dXJuKGlmZWxzZShjbHVzdGVyc1tzSURdID09IDEsIDEsIDApKQp9Cgpjb2xDbGFzcyA8LSBzYXBwbHkocGVyc29ucywgZ2V0Q2x1c3Rlck5hbWUpCmRmX0RyaXZlMyRjbHNQUCA8LSBhcy5mYWN0b3IoY29sQ2xhc3MpCmBgYAoKYGBge3J9CiMgaW5zdGFsbC5wYWNrYWdlcygicmFuZG9tRm9yZXN0IikKIyBpbnN0YWxsLnBhY2thZ2VzKCJNTG1ldHJpY3MiKQojIGluc3RhbGwucGFja2FnZXMoImNhcmV0IikKbGlicmFyeShyYW5kb21Gb3Jlc3QpCmxpYnJhcnkoUk9DUikKbGlicmFyeShjYXJldCkKCiMgVHJhaW4KdHJhaW5BbmRUZXN0TW9kZWwgPC0gZnVuY3Rpb24ocERhdGEpIHsKICBwU2VsZWN0ZWQgPC0gcERhdGEKICBwU2VsZWN0ZWQkU3ViamVjdCA8LSBOVUxMCiAgIyBwU2VsZWN0ZWQkYFN0ZWVyaW5nLi5zdGQuLmAgPC0gTlVMTAogIAogICMgVmlldyhwU2VsZWN0ZWQpCiAgCiAgbkNoaWNrZW4gPSBucm93KHBTZWxlY3RlZFtwU2VsZWN0ZWQkY2xzUFAgPT0gMSxdKQogIG5Ob3JtYWwgPSBucm93KHBTZWxlY3RlZFtwU2VsZWN0ZWQkY2xzUFAgPT0gMCxdKQogIAogIHByaW50KHBhc3RlKCJDaGlja2VuID0iLCBuQ2hpY2tlbikpCiAgcHJpbnQocGFzdGUoIk5vcm1hbCA9Iiwgbk5vcm1hbCkpCiAgCiAgIyBTcGxpdCBkYXRhc2V0CiAgc2V0LnNlZWQoNDMpCiAgbl9mb2xkcyA9IDQKICAKICBmb2xkcyA8LSBjcmVhdGVGb2xkcyhmYWN0b3IocFNlbGVjdGVkJGNsc1BQKSwgayA9IG5fZm9sZHMsIGxpc3QgPSBGQUxTRSkKICAjIGZvbGRzIDwtIGN1dChzZXEoMSxucm93KHBTZWxlY3RlZCkpLCBicmVha3M9bl9mb2xkcyxsYWJlbHM9RkFMU0UpCiAgcFNlbGVjdGVkJGZvbGQgPC0gZm9sZHMKICAKICBrZl9hY2MgPC0gZG91YmxlKG5fZm9sZHMpCiAga2ZfcHJlYyA8LSBkb3VibGUobl9mb2xkcykKICBrZl9yZWNhbGwgPC0gZG91YmxlKG5fZm9sZHMpCiAga2ZfZjEgPC0gZG91YmxlKG5fZm9sZHMpCiAga2ZfbnB2IDwtIGRvdWJsZShuX2ZvbGRzKQogIAogIGZvciAoaSBpbiAxOm5fZm9sZHMpIHsKICAgICMgdHJhaW5faW5kIDwtIHNhbXBsZShzZXFfbGVuKG5yb3cocFNlbGVjdGVkKSksIHNpemUgPSBzbXBfc2l6ZSkKICAgICMgdHJhaW4gPC0gcFNlbGVjdGVkW3RyYWluX2luZCwgXQogICAgIyB0ZXN0IDwtIHBTZWxlY3RlZFstdHJhaW5faW5kLCBdCiAgICAKICAgICMgdGVzdF9pbmQgPC0gd2hpY2goZm9sZHM9PWksIGFyci5pbmQ9VCkKICAgIHRyYWluIDwtIHBTZWxlY3RlZFtwU2VsZWN0ZWQkZm9sZCAhPSBpLCBdICU+JSBzZWxlY3QoLWZvbGQpCiAgICB0ZXN0IDwtIHBTZWxlY3RlZFtwU2VsZWN0ZWQkZm9sZCA9PSBpLCBdICU+JSBzZWxlY3QoLWZvbGQpCiAgICAKICAgIHByaW50KHBhc3RlKCJUcmFpbiA9IiwgbnJvdyh0cmFpbiksICJQb3MgPSIsIG5yb3codHJhaW5bdHJhaW4kY2xzUFAgPT0gMSxdKSwgIk5lZyA9IiwgbnJvdyh0cmFpblt0cmFpbiRjbHNQUCA9PSAwLF0pKSkKICAgIHByaW50KHBhc3RlKCJUZXN0ID0iLCBucm93KHRlc3QpLCAiUG9zID0iLCBucm93KHRlc3RbdGVzdCRjbHNQUCA9PSAxLF0pLCAiTmVnID0iLCBucm93KHRlc3RbdGVzdCRjbHNQUCA9PSAwLF0pKSkKICAgIAogICAgIyBNb2RlbCBUcmFpbgogICAgbW9kZWwgPC0gcmFuZG9tRm9yZXN0KGNsc1BQfi4sIGRhdGEgPSB0cmFpbiwgaW1wb3J0YW5jZSA9IFRSVUUsIG50cmVlPTEwKQogICAgcHJpbnQoaW1wb3J0YW5jZShtb2RlbCkpCiAgICAjIHByaW50KG1vZGVsKQogICAgCiAgICAjIFRlc3QKICAgIHRlc3RYIDwtIHNlbGVjdCh0ZXN0LCAtY2xzUFApCiAgICBwcmVkWSA8LSBwcmVkaWN0KG1vZGVsLCB0ZXN0WCkKICAgIHRlc3RZIDwtIHRlc3QkY2xzUFAKICAgIAogICAgI3ByaW50KGxldmVscyhwcmVkWSkpCiAgICAjcHJpbnQobGV2ZWxzKHRlc3RZKSkKICAgIAogICAgIyBFdmFsdWF0ZQogICAgIyBwcmludCh0YWJsZShwcmVkWSwgdGVzdFkpKQogICAgCiAgICBrZl9hY2NbaV0gPC0gbWVhbihwcmVkWT09dGVzdFkpCiAgICBrZl9yZWNhbGxbaV0gPC0gc2Vuc2l0aXZpdHkocHJlZFksIHRlc3RZKQogICAga2ZfcHJlY1tpXSA8LSBwb3NQcmVkVmFsdWUocHJlZFksIHRlc3RZLCBwb3NpdGl2ZSA9IDEpCiAgICBrZl9mMVtpXSA8LSAoMiAqIGtmX3JlY2FsbFtpXSAqIGtmX3ByZWNbaV0pIC8gKGtmX3JlY2FsbFtpXSArIGtmX3ByZWNbaV0pCiAgICBrZl9ucHZbaV0gPC0gbmVnUHJlZFZhbHVlKHByZWRZLCB0ZXN0WSwgcG9zaXRpdmUgPSAxKQogICAgCiAgICAjIHByaW50KHBhc3RlKCJQZXJmOiIsIGtmX2FjY1tpXSwga2ZfcmVjYWxsW2ldLCBrZl9wcmVjW2ldLCBrZl9mMVtpXSwga2ZfbnB2W2ldKSkKICB9CiAgCiAgIyBYR0IKICBwYXJhbSA8LSBsaXN0KG9iamVjdGl2ZSAgICAgICA9ICJiaW5hcnk6bG9naXN0aWMiLCAKICAgICAgICAgICAgICAgYm9vc3RlciAgICAgICAgICA9ICJnYnRyZWUiLAogICAgICAgICAgICAgICBldmFsX21ldHJpYyAgICAgID0gImF1YyIsCiAgICAgICAgICAgICAgIGV0YSAgICAgICAgICAgICAgPSAwLjEsCiAgICAgICAgICAgICAgIG1heF9kZXB0aCAgICAgICAgPSA1LAogICAgICAgICAgICAgICBnYW1tYSAgICAgICAgICAgID0wLjgsCiAgICAgICAgICAgICAgIG1pbl9jaGlsZF93ZWlnaHQgPSAzLAogICAgICAgICAgICAgICBzdWJzYW1wbGUgICAgICAgID0gMSwKICAgICAgICAgICAgICAgY29sc2FtcGxlX2J5dHJlZSA9IDAuNSwKICAgICAgICAgICAgICAgc3RyYXRpZmllZCAgICAgICA9IEYKICApCiAgcFNlbGVjdGVkIDwtIHBTZWxlY3RlZCAlPiUgbXV0YXRlKGNsc1BQPWlmZWxzZShjbHNQUD09MSwgMSwgMCkpCiAgCiAgIyBBVUMKICAjIGF1Y3MgPSBjKCkKICAjIHhnYl9tID0geGdiLmN2KCAgIHBhcmFtcyAgICAgICAgICAgICAgID0gcGFyYW0sCiAgIyAgICAgICAgICAgICAgICAgICBkYXRhID0gYXMubWF0cml4KHBTZWxlY3RlZCAlPiUgc2VsZWN0KC1jbHNQUCkpICwKICAjICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIHBTZWxlY3RlZCRjbHNQUCwKICAjICAgICAgICAgICAgICAgICAgIG5yb3VuZHMgICAgICAgICAgICAgPSA1MDAsCiAgIyAgICAgICAgICAgICAgICAgICB2ZXJib3NlICAgICAgICAgICAgID0gRiwKICAjICAgICAgICAgICAgICAgICAgIHByZWRpY3Rpb24gICAgICAgICAgPSBULAogICMgICAgICAgICAgICAgICAgICAgbWF4aW1pemUgICAgICAgICAgICA9IFQsCiAgIyAgICAgICAgICAgICAgICAgICBuZm9sZCA9IG5fZm9sZHMsCiAgIyAgICAgICAgICAgICAgICAgICBtZXRyaWNzICA9ICJhdWMiLAogICMgICAgICAgICAgICAgICAgICAgZWFybHlfc3RvcHBpbmdfcm91bmRzID0gMTAwLAogICMgICAgICAgICAgICAgICAgICAgc2NhbGVfcG9zX3dlaWdodCA9IDEpCiAgIyBhdWNzID0gIGMoYXVjcyxhcy5udW1lcmljKHhnYl9tJGV2YWx1YXRpb25fbG9nW3hnYl9tJGJlc3RfaXRlcmF0aW9uLCJ0ZXN0X2F1Y19tZWFuIl0pKQogIAogICMgR2V0IGF2ZXJhZ2UgcGVyZm9ybWFuY2UKICBhY2MgPC0gbWVhbihrZl9hY2MpCiAgcHJlYyA8LSBtZWFuKGtmX3ByZWMpCiAgcmVjYWxsIDwtIG1lYW4oa2ZfcmVjYWxsKQogIGYxIDwtIG1lYW4oa2ZfZjEpCiAgbnB2IDwtIG1lYW4oa2ZfbnB2KQogICMgYXVjIDwtIG1lYW4oYXVjcykKIAogICMgUmV0dXJuIAogIHJ0biA8LSBsaXN0KAogICAgYWNjdXJhY3k9YWNjLAogICAgcmVjYWxsPXJlY2FsbCwKICAgIHByZWNpc2lvbj1wcmVjLAogICAgZjE9ZjEsCiAgICBucHY9bnB2CiAgICAjIGF1Yz1hdWMKICApCiAgCiAgcHJpbnQocnRuKQp9Cgp0cmFpbkFuZFRlc3RNb2RlbChkZl9Ecml2ZTMpCmBgYAoKCmBgYHtyfQpsaWJyYXJ5KGNhcmV0KQpsaWJyYXJ5KFZHQU0pCgpOVU1fRkVBVFVSRVMgPSA4CgpwU2VsZWN0ZWQgPC0gZGZfRHJpdmUzCnBTZWxlY3RlZCRTdWJqZWN0IDwtIE5VTEwKCmZpdCA8LSB2Z2xtKGNsc1BQfi4sIGZhbWlseT1tdWx0aW5vbWlhbCwgZGF0YT1wU2VsZWN0ZWQpCiMgc3VtbWFyaXplIHRoZSBmaXQKc3VtbWFyeShmaXQpCgpwcm9iYWJpbGl0aWVzIDwtIHByZWRpY3QoZml0LCBwU2VsZWN0ZWRbLDE6TlVNX0ZFQVRVUkVTXSwgdHlwZT0icmVzcG9uc2UiKQpwcmVkaWN0aW9ucyA8LSBhcHBseShwcm9iYWJpbGl0aWVzLCAxLCB3aGljaC5tYXgpCnByZWRpY3Rpb25zW3doaWNoKHByZWRpY3Rpb25zPT0iSGlnaCIpXSA8LSBsZXZlbHMocFNlbGVjdGVkJGNsc1BQKVsxXQpwcmVkaWN0aW9uc1t3aGljaChwcmVkaWN0aW9ucz09IkxvdyIpXSA8LSBsZXZlbHMocFNlbGVjdGVkJGNsc1BQKVsyXQojIHN1bW1hcml6ZSBhY2N1cmFjeQp0YWJsZShwcmVkaWN0aW9ucywgcFNlbGVjdGVkJGNsc1BQKQoKIyB4IDwtIHBTZWxlY3RlZFssMTpOVU1fRkVBVFVSRVNdCiMgeSA8LSBwU2VsZWN0ZWRbLE5VTV9GRUFUVVJFUyArIDFdCiMgIyBmaXQgbW9kZWwKIyBmaXQgPC0gcGxzZGEoeCwgeSwgcHJvYk1ldGhvZD0iQmF5ZXMiKQojICMgc3VtbWFyaXplIHRoZSBmaXQKIyBzdW1tYXJ5KGZpdCkKIyAjIG1ha2UgcHJlZGljdGlvbnMKIyBwcmVkaWN0aW9ucyA8LSBwcmVkaWN0KGZpdCwgcFNlbGVjdGVkWywxOk5VTV9GRUFUVVJFU10pCiMgIyBzdW1tYXJpemUgYWNjdXJhY3kKIyB0YWJsZShwcmVkaWN0aW9ucywgcFNlbGVjdGVkJGNsc1BQKQoKYGBgCgoKYGBge3J9CmxpYnJhcnkoY2FyZXQpCgpwU2VsZWN0ZWQgPC0gZGZfRHJpdmUzCnBTZWxlY3RlZCRTdWJqZWN0IDwtIE5VTEwKcFNlbGVjdGVkJGNsc1BQIDwtIGFzLm51bWVyaWMoY29sQ2xhc3MpCgojIGRlZmluZSB0cmFpbmluZyBjb250cm9sCnRyYWluX2NvbnRyb2wgPC0gdHJhaW5Db250cm9sKG1ldGhvZCA9ICJjdiIsIG51bWJlciA9IDQpCgojIHRyYWluIHRoZSBtb2RlbCBvbiB0cmFpbmluZyBzZXQKbW9kZWwgPC0gdHJhaW4oY2xzUFAgfiAuLAogICAgICAgICAgICAgICBkYXRhID0gcFNlbGVjdGVkLAogICAgICAgICAgICAgICB0ckNvbnRyb2wgPSB0cmFpbl9jb250cm9sLAogICAgICAgICAgICAgICBtZXRob2QgPSAiZ2xtIiwKICAgICAgICAgICAgICAgZmFtaWx5PWJpbm9taWFsKCkpCgojIHByaW50IGN2IHNjb3JlcwpzdW1tYXJ5KG1vZGVsKQoKcHJlZFRyYWluID0gcHJlZGljdChtb2RlbCwgbmV3ZGF0YT1wU2VsZWN0ZWQsIHR5cGU9InJhdyIpCnRhYmxlKHBTZWxlY3RlZCRjbHNQUCwgcHJlZFRyYWluID4gMC41KQoKYGBgCgpgYGB7cn0KZm9ybXVsYSA9IGNsc1BQIH4gLgptb2RlbGxtIDwtIGdsbShmb3JtdWxhLCBkYXRhPXBTZWxlY3RlZCkKcGxvdChtb2RlbGxtKQpzdW1tYXJ5KG1vZGVsKQpgYGAKCgo=